package com.menghao.rpc.config;

import com.menghao.rpc.RpcConstants;
import com.menghao.rpc.consumer.handle.JdkProxyFactory;
import com.menghao.rpc.consumer.discovery.http.HttpReferenceRepository;
import com.menghao.rpc.consumer.discovery.ProviderDiscovery;
import com.menghao.rpc.consumer.discovery.ReferenceRepository;
import com.menghao.rpc.consumer.discovery.tcp.TcpReferenceRepository;
import com.menghao.rpc.consumer.handle.tcp.InvocationContextContainer;
import com.menghao.rpc.provider.handle.tcp.RpcRequestMsgHandler;
import com.menghao.rpc.netty.TcpClient;
import com.menghao.rpc.netty.TcpConnectionContainer;
import com.menghao.rpc.netty.TcpMessageHandler;
import com.menghao.rpc.consumer.handle.tcp.RpcResponseMsgHandler;
import com.menghao.rpc.spring.BeansManager;
import com.menghao.rpc.spring.ReferenceAutowired;
import com.menghao.rpc.spring.RpcRequestConvert;
import com.menghao.rpc.spring.RpcResponseConvert;
import com.menghao.rpc.zookeeper.CuratorClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.Assert;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * <p>服务消费方自配置类.<br>
 *
 * @author MarvelCode.
 */
@Configuration
@EnableConfigurationProperties(RpcProperties.class)
@ConditionalOnProperty(prefix = "marvel.rpc", name = "consumerEnable", havingValue = "true")
public class ConsumerAutoConfig {

    private static final Logger LOGGER = LoggerFactory.getLogger(ConsumerAutoConfig.class);

    private RpcProperties rpcProperties;

    public ConsumerAutoConfig(RpcProperties rpcProperties) throws IOException {
        Assert.hasText(rpcProperties.getZkServerHost(), "please appoint zookeeper server address");
        this.rpcProperties = rpcProperties;
    }

    @Bean
    public JdkProxyFactory jdkProxyFactory() {
        return new JdkProxyFactory();
    }

    @Bean
    public ProviderDiscovery providerDiscovery(CuratorClient curatorClient) {
        return new ProviderDiscovery(curatorClient);
    }

    @Bean
    public ReferenceAutowired referenceAutowired(ReferenceRepository referenceRepository) {
        return new ReferenceAutowired(referenceRepository);
    }

    @Bean
    @ConditionalOnMissingBean(BeansManager.class)
    public BeansManager beansManager() {
        return BeansManager.getInstance();
    }

    @Bean(destroyMethod = "close")
    @ConditionalOnMissingBean(CuratorClient.class)
    public CuratorClient curatorClient() {
        return new CuratorClient(rpcProperties.getZkServerHost(),
                rpcProperties.getSessionTimeout(), rpcProperties.getConnectionTimeout(),
                rpcProperties.getZkRootPath());
    }

    @Configuration
    @ConditionalOnProperty(prefix = "marvel.rpc", name = "type", havingValue = "tcp")
    @EnableConfigurationProperties(RpcTcpProperties.class)
    public static class TcpModeAutoConfig {

        private RpcTcpProperties tcpProperties;

        @Autowired
        public TcpModeAutoConfig(RpcTcpProperties tcpProperties) {
            this.tcpProperties = tcpProperties;
        }

        @Bean
        @DependsOn("beansManager")
        public TcpConnectionContainer connectionContainer() {
            return new TcpConnectionContainer();
        }

        @Bean
        @DependsOn("beansManager")
        public ReferenceRepository tcpReferenceRepository(JdkProxyFactory jdkProxyFactory, ProviderDiscovery providerDiscovery) {
            LOGGER.info(RpcConstants.LOG_RPC_PREFIX + "service consumer use type-tcp");
            return new TcpReferenceRepository(jdkProxyFactory, providerDiscovery);
        }

        @Bean
        public InvocationContextContainer contextContainer() {
            return new InvocationContextContainer();
        }

        @Bean(initMethod = "initBootstrap")
        public TcpClient tcpClient() {
            List<TcpMessageHandler> messageHandlers = new ArrayList<>(2);
            messageHandlers.add(new RpcRequestMsgHandler());
            messageHandlers.add(new RpcResponseMsgHandler());
            return new TcpClient(tcpProperties.getConnectTimeout(),
                    tcpProperties.getMaxFrameLength(),
                    tcpProperties.getReadIdle(),
                    tcpProperties.getWritIdle(),
                    messageHandlers);
        }
    }

    @Configuration
    @ConditionalOnProperty(prefix = "marvel.rpc", name = "type", havingValue = "http")
    public static class HttpModeAutoConfig {

        @Bean
        public RestTemplate restTemplate() {
            return new RestTemplate(new ArrayList<HttpMessageConverter<?>>(
                    Arrays.asList(new RpcResponseConvert(MediaType.TEXT_PLAIN), new RpcRequestConvert(MediaType.TEXT_PLAIN))
            ));
        }

        @Bean
        public ReferenceRepository httpReferenceRepository(JdkProxyFactory jdkProxyFactory, ProviderDiscovery providerDiscovery) {
            LOGGER.info(RpcConstants.LOG_RPC_PREFIX + "service consumer use type-http");
            return new HttpReferenceRepository(jdkProxyFactory, providerDiscovery);
        }
    }
}
